﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net;
using System.Web.Services.Protocols;
using System.Xml;
using Microsoft.Practices.EnterpriseLibrary.Logging;
using QueueManagerHelper.Exchange2007;

namespace QueueManagerHelper
{
    public class EExchange2007HelperException : Exception
    {
        public EExchange2007HelperException(string message) : base(message) { }
        public EExchange2007HelperException(string message, Exception innerException) : base(message, innerException) { }
    }
    
    public class Exchange2007Helper
    {
        CustomReader _service;

        private FindItemType findItem = null;
        private ExchangeServiceBinding Service
        {
            get { return _service; }
        }

        public Exchange2007Helper(string url, string domain, string username, string password)
        {
            _service = new CustomReader();
            _service.Credentials = new NetworkCredential(username, password, domain);
            _service.Url = url;
        }

        public void GetAttachmentsInMessage(ItemIdType messageId, out List<FileAttachmentType> fileAttachments, out List<ItemAttachmentType> itemAttachments)
        {
            string functionName = GetType().ToString() + " GetAttachmentsInMessage: ";
            Logger.Write(functionName + "Enter", "Trace", 3);
            try
            {
                fileAttachments = new List<FileAttachmentType>();
                itemAttachments = new List<ItemAttachmentType>();
                List<FileAttachmentType> _result = new List<FileAttachmentType>();
                GetItemType getMessageRequest = BuildGetAttachmentsInMessageQuery(messageId);
                GetItemResponseType getItemResponse = _service.GetItem(getMessageRequest);
                ItemInfoResponseMessageType getItemResponseMessage = getItemResponse.ResponseMessages.Items[0] as ItemInfoResponseMessageType;

                if (getItemResponseMessage.ResponseCode != ResponseCodeType.NoError)
                    return;
                ItemType item = getItemResponseMessage.Items.Items[0];
                if (item.Attachments == null || item.Attachments.Length == 0)
                    return;
                List<RequestAttachmentIdType> attachmentIds = new List<RequestAttachmentIdType>();
                for (int attachmentIndex = 0; attachmentIndex < item.Attachments.Length; attachmentIndex++)
                {
                    if (item.Attachments[attachmentIndex] is FileAttachmentType)
                    {
                        FileAttachmentType almostAnAttachment = item.Attachments[attachmentIndex] as FileAttachmentType;
                        if (almostAnAttachment != null)
                        {
                            RequestAttachmentIdType requestId = new RequestAttachmentIdType();
                            requestId.Id = almostAnAttachment.AttachmentId.Id;
                            attachmentIds.Add(requestId);
                        }
                    }
                    else
                    {
                        ItemAttachmentType almostAnAttachment2 = item.Attachments[attachmentIndex] as ItemAttachmentType;
                        MessageType originalMessage = GetOriginalMessage(item.Attachments[attachmentIndex].AttachmentId);
                        if (originalMessage != null)
                        {
                            GetAttachmentType getAttachmentRequest2 = new GetAttachmentType();
                            getAttachmentRequest2.AttachmentShape = new AttachmentResponseShapeType();
                            getAttachmentRequest2.AttachmentIds = new RequestAttachmentIdType[] { almostAnAttachment2.AttachmentId };
                            GetAttachmentResponseType getAttachmentResponse2 = _service.GetAttachment(getAttachmentRequest2);
                            foreach (AttachmentInfoResponseMessageType attachmentResponseMessage in getAttachmentResponse2.ResponseMessages.Items)
                                if (attachmentResponseMessage.ResponseCode == ResponseCodeType.NoError)
                                    itemAttachments.Add(attachmentResponseMessage.Attachments[0] as ItemAttachmentType);
                        }
                    }
                }
                if (attachmentIds.Count == 0)
                    return;

                GetAttachmentType getAttachmentRequest = new GetAttachmentType();
                getAttachmentRequest.AttachmentShape = new AttachmentResponseShapeType();
                getAttachmentRequest.AttachmentIds = attachmentIds.ToArray();
                GetAttachmentResponseType getAttachmentResponse = _service.GetAttachment(getAttachmentRequest);
                foreach (AttachmentInfoResponseMessageType attachmentResponseMessage in getAttachmentResponse.ResponseMessages.Items)
                    if (attachmentResponseMessage.ResponseCode == ResponseCodeType.NoError)
                        fileAttachments.Add(attachmentResponseMessage.Attachments[0] as FileAttachmentType);
            }
            catch (Exception ex)
            {
                Logger.Write(functionName + "Error: " + ex.ToString(), "Error", 5, 999, TraceEventType.Error);
                throw;
            }
            finally
            {
                Logger.Write(functionName + "Exit", "Trace", 3);
            }
        }

        private GetItemType BuildGetAttachmentsInMessageQuery(ItemIdType messageId)
        {
            string functionName = GetType().ToString() + " BuildGetAttachmentsInMessageQuery: ";
            Logger.Write(functionName + "Enter", "Trace", 3);
            try
            {
                GetItemType getMessageRequest = new GetItemType();
                getMessageRequest.ItemIds = new BaseItemIdType[] { messageId };
                getMessageRequest.ItemShape = new ItemResponseShapeType();
                getMessageRequest.ItemShape.BaseShape = DefaultShapeNamesType.Default;
                PathToUnindexedFieldType hasAttachmentsPath = new PathToUnindexedFieldType();
                hasAttachmentsPath.FieldURI = UnindexedFieldURIType.itemAttachments;
                PathToUnindexedFieldType attachmentsPath = new PathToUnindexedFieldType();
                attachmentsPath.FieldURI = UnindexedFieldURIType.itemAttachments;
                getMessageRequest.ItemShape.AdditionalProperties = new BasePathToElementType[] { hasAttachmentsPath, attachmentsPath };
                return getMessageRequest;
            }
            catch (Exception ex)
            {
                Logger.Write(functionName + "Error: " + ex.ToString(), "Error", 5, 999, TraceEventType.Error);
                throw;
            }
            finally
            {
                Logger.Write(functionName + "Exit", "Trace", 3);
            }
        }

        private MessageType GetOriginalMessage(AttachmentIdType attachmentId)
        {
            string functionName = GetType().ToString() + " GetOriginalMessage: ";
            Logger.Write(functionName + "Enter", "Trace", 3);
            try
            {
                GetAttachmentType getAttachment = new GetAttachmentType();
                getAttachment.AttachmentShape = new AttachmentResponseShapeType();
                getAttachment.AttachmentShape.IncludeMimeContent = true;
                getAttachment.AttachmentShape.IncludeMimeContentSpecified = true;
                getAttachment.AttachmentIds = new AttachmentIdType[] { attachmentId };
                GetAttachmentResponseType getAttachmentResponse = _service.GetAttachment(getAttachment);
                if (getAttachmentResponse.ResponseMessages == null || getAttachmentResponse.ResponseMessages.Items == null)
                    return null;
                MessageType originalMessage = null;
                foreach (AttachmentInfoResponseMessageType atAttachmentResponse in getAttachmentResponse.ResponseMessages.Items)
                    if (atAttachmentResponse.ResponseClass == ResponseClassType.Success)
                    {
                        ItemAttachmentType itIemAttachmentType = atAttachmentResponse.Attachments[0] as ItemAttachmentType;
                        originalMessage = (MessageType)itIemAttachmentType.Item;
                    }
                return originalMessage;
            }
            catch (Exception ex)
            {
                Logger.Write(functionName + "Error: " + ex.ToString(), "Error", 5, 999, TraceEventType.Error);
                throw;
            }
            finally
            {
                Logger.Write(functionName + "Exit", "Trace", 3);
            }
        }

        public void SetItemReadStatus(ItemIdType itemId, bool isRead)
        {
            string functionName = GetType().ToString() + " SetItemReadStatus: ";
            Logger.Write(functionName + "Enter", "Trace", 3);
            try
            {
                MessageType messageType = new MessageType();
                messageType.IsRead = isRead;
                messageType.IsReadSpecified = true;
                SetItemFieldType setItemAsReadRequest = new SetItemFieldType();
                setItemAsReadRequest.Item = new PathToUnindexedFieldType();
                (setItemAsReadRequest.Item as PathToUnindexedFieldType).FieldURI = UnindexedFieldURIType.messageIsRead;
                setItemAsReadRequest.Item1 = messageType;
                UpdateItemType updateRequest = new UpdateItemType();
                updateRequest.ItemChanges = new ItemChangeType[1];
                updateRequest.MessageDisposition = MessageDispositionType.SaveOnly;
                updateRequest.MessageDispositionSpecified = true;
                ItemChangeType workRequest = new ItemChangeType();
                workRequest.Item = itemId;
                workRequest.Updates = new ItemChangeDescriptionType[1];
                workRequest.Updates[0] = setItemAsReadRequest;
                updateRequest.ItemChanges[0] = workRequest;
                UpdateItemResponseType updateRequestResponse = _service.UpdateItem(updateRequest);
                foreach (ItemInfoResponseMessageType responseMessage in updateRequestResponse.ResponseMessages.Items)
                    if (responseMessage.ResponseClass == ResponseClassType.Error)
                        throw new EExchange2007HelperException(responseMessage.MessageText);
            }
            catch (Exception ex)
            {
                Logger.Write(functionName + "Warning: " + ex.ToString(), "Warning", 5, 999, TraceEventType.Warning);
                throw;
            }
            finally
            {
                Logger.Write(functionName + "Exit", "Trace", 3);
            }
        }

        public List<MessageType> GetFolderItems(string folderName, string addressFrom)
        {
            findItem = BuildGetFolderItemsQuery(folderName, addressFrom);
            return GetFolderItems(folderName);
        }
        public List<MessageType> GetFolderItems(string folderName)
        {
            string functionName = GetType().ToString() + " GetFolderItems: ";
            Logger.Write(functionName + "Enter", "Trace", 3);
            try
            {
                //only build the findItem the first time: it's very resource intensive
                if (findItem  == null) 
                    findItem = BuildGetFolderItemsQuery(folderName);
                Logger.Write(functionName + "Executing FindItem", "Trace", 1);
                FindItemResponseType response = _service.FindItem(findItem);
                Logger.Write(functionName + "FindItem executed", "Trace", 1);
                if (response.ResponseMessages.Items == null)
                    return null;
                FindItemResponseMessageType responseMessage = response.ResponseMessages.Items[0] as FindItemResponseMessageType;
                if (responseMessage == null)
                    return null;
                if (responseMessage.ResponseCode != ResponseCodeType.NoError)
                    throw new EExchange2007HelperException(responseMessage.MessageText);
                else
                {
                    ArrayOfRealItemsType items = responseMessage.RootFolder.Item as ArrayOfRealItemsType;
                    if (items == null || items.Items == null)
                        return null;
                    else
                    {
                        Logger.Write(functionName + "Items found: " + items.Items.Length, "Trace", 1);
                        List<MessageType> result = new List<MessageType>();
                        foreach (var item in items.Items)
                            if (item is MessageType)
                            {
                                MessageType messageItem = (MessageType)item;
                                GetItemType getMessageRequest = new GetItemType();
                                getMessageRequest.ItemIds = new BaseItemIdType[] { messageItem.ItemId };
                                getMessageRequest.ItemShape = new ItemResponseShapeType();
                                getMessageRequest.ItemShape.BaseShape = DefaultShapeNamesType.AllProperties;
                                Logger.Write(functionName + "Executing GetItem", "Trace", 1);
                                GetItemResponseType getItemResponse = _service.GetItem(getMessageRequest);
                                Logger.Write(functionName + "GetItem executed", "Trace", 1);
                                ItemInfoResponseMessageType getItemResponseMessage = getItemResponse.ResponseMessages.Items[0] as ItemInfoResponseMessageType;
                                if (getItemResponseMessage.ResponseCode == ResponseCodeType.NoError)
                                    result.Add((MessageType)getItemResponseMessage.Items.Items[0]);
                            }
                            else
                                DeleteItems(new BaseItemIdType[] { item.ItemId }, DisposalType.MoveToDeletedItems);
                        return result;
                    }
                }
            }
            catch (Exception ex)
            {
                Logger.Write(functionName + "Error: " + ex.ToString(), "Error", 5, 999, TraceEventType.Error);
                throw;
            }
            finally
            {
                Logger.Write(functionName + "Exit", "Trace", 3);
            }
        }

        private FindItemType BuildGetFolderItemsQuery(string folderName)
        {
            string functionName = GetType().ToString() + " BuildGetFolderItemsQuery: ";
            Logger.Write(functionName + "Enter", "Trace", 3);
            try
            {
                FindItemType findItem = new FindItemType();
                findItem.ItemShape = new ItemResponseShapeType();
                ItemResponseShapeType itemResponseProperties = new ItemResponseShapeType();
                //use the default shape to get the response
                itemResponseProperties.BaseShape = DefaultShapeNamesType.Default;
                findItem.ItemShape = itemResponseProperties;
                //set the inbox as the parent search folder 
                BaseFolderIdType folderId = GetFolderId(folderName);
                findItem.ParentFolderIds = new BaseFolderIdType[] { folderId };
                findItem.Traversal = ItemQueryTraversalType.Shallow;
                //restriction: only unread items
                RestrictionType restrict = new RestrictionType();
                IsEqualToType isEqTo = new IsEqualToType();
                PathToUnindexedFieldType ptuift = new PathToUnindexedFieldType();
                ptuift.FieldURI = UnindexedFieldURIType.messageIsRead;
                isEqTo.Item = ptuift;
                FieldURIOrConstantType msgReadYes = new FieldURIOrConstantType();
                msgReadYes.Item = new ConstantValueType();
                //1= boolean yes; so you'll get the list of read messages
                (msgReadYes.Item as ConstantValueType).Value = "0";
                isEqTo.FieldURIOrConstant = msgReadYes;
                restrict.Item = isEqTo;
                findItem.Restriction = restrict;
                findItem.SortOrder = new[]
                { 
                    new FieldOrderType
                    { 
                        Item = new PathToUnindexedFieldType
                        { 
                            FieldURI = UnindexedFieldURIType.itemDateTimeReceived
                        }, 
                        Order= SortDirectionType.Ascending,
                    }
                };
                return findItem;
            }
            catch (Exception ex)
            {
                Logger.Write(functionName + "Error: " + ex.ToString(), "Error", 5, 999, TraceEventType.Error);
                throw;
            }
            finally
            {
                Logger.Write(functionName + "Exit", "Trace", 3);
            }
        }

        private FindItemType BuildGetFolderItemsQuery(string folderName, string addressFrom)
        {
            string functionName = GetType().ToString() + " BuildGetFolderItemsQuery: ";
            Logger.Write(functionName + "Enter", "Trace", 3);
            try
            {
                FindItemType findItem = new FindItemType();
                findItem.ItemShape = new ItemResponseShapeType();
                ItemResponseShapeType itemResponseProperties = new ItemResponseShapeType();
                //use the default shape to get the response
                itemResponseProperties.BaseShape = DefaultShapeNamesType.Default;
                findItem.ItemShape = itemResponseProperties;
                //set the inbox as the parent search folder 
                BaseFolderIdType folderId = GetFolderId(folderName);
                findItem.ParentFolderIds = new BaseFolderIdType[] { folderId };
                findItem.Traversal = ItemQueryTraversalType.Shallow;
                //restriction: only unread items
                RestrictionType restrict = new RestrictionType();
                IsEqualToType isEqTo = new IsEqualToType();
                PathToUnindexedFieldType ptuift = new PathToUnindexedFieldType();
                ptuift.FieldURI = UnindexedFieldURIType.messageFrom;
                isEqTo.Item = ptuift;
                FieldURIOrConstantType msgFrom = new FieldURIOrConstantType();
                msgFrom.Item = new ConstantValueType();
                (msgFrom.Item as ConstantValueType).Value = addressFrom;
                isEqTo.FieldURIOrConstant = msgFrom;
                restrict.Item = isEqTo;
                findItem.Restriction = restrict;
                findItem.SortOrder = new[]
                { 
                    new FieldOrderType
                    { 
                        Item = new PathToUnindexedFieldType
                        { 
                            FieldURI = UnindexedFieldURIType.itemDateTimeReceived
                        }, 
                        Order= SortDirectionType.Ascending,
                    }
                };
                return findItem;
            }
            catch (Exception ex)
            {
                Logger.Write(functionName + "Error: " + ex.ToString(), "Error", 5, 999, TraceEventType.Error);
                throw;
            }
            finally
            {
                Logger.Write(functionName + "Exit", "Trace", 3);
            }
        }

        public void DeleteItems(BaseItemIdType[] idItemstoDelete, DisposalType diType)
        {
            string functionName = GetType().ToString() + " DeleteItems: ";
            Logger.Write(functionName + "Enter", "Trace", 3);
            try
            {
                DeleteItemType deleteItemType = new DeleteItemType();
                deleteItemType.ItemIds = idItemstoDelete;
                deleteItemType.DeleteType = diType;
                deleteItemType.SendMeetingCancellationsSpecified = true;
                deleteItemType.SendMeetingCancellations = CalendarItemCreateOrDeleteOperationType.SendToNone;
                DeleteItemResponseType deleteItemResponseType = _service.DeleteItem(deleteItemType);
                ResponseMessageType[] diResponseMessages = deleteItemResponseType.ResponseMessages.Items;
                foreach (ResponseMessageType repMessage in diResponseMessages)
                    if (repMessage.ResponseClass != ResponseClassType.Success)
                        throw new EExchange2007HelperException("Error on delete of item: " + repMessage.MessageText);
            }
            catch (Exception ex)
            {
                Logger.Write(functionName + "Error: " + ex.ToString(), "Error", 5, 999, TraceEventType.Error);
                throw;
            }
            finally
            {
                Logger.Write(functionName + "Exit", "Trace", 3);
            }
        }

        private FolderIdType GetFolderId(string folderName)
        {
            string functionName = GetType().ToString() + " GetFolderId: ";
            Logger.Write(functionName + "Enter", "Trace", 3);
            try
            {
                FindFolderType findFolderType = BuildGetFolderIdQuery(folderName);
                Logger.Write(functionName + "Executing FindFolder request", "Trace", 1);
                FindFolderResponseType findFolderResponse = Service.FindFolder(findFolderType);
                Logger.Write(functionName + "Returned FindFolder request", "Trace", 1);
                if (findFolderResponse.ResponseMessages.Items.Length == 0)
                    return null;
                foreach (ResponseMessageType response in findFolderResponse.ResponseMessages.Items)
                    if (response.ResponseClass == ResponseClassType.Success)
                    {
                        FindFolderResponseMessageType findResponse = response as FindFolderResponseMessageType;
                        foreach (BaseFolderType folderType in findResponse.RootFolder.Folders)
                            return folderType.FolderId;
                    }
                return null;
            }
            catch (Exception ex)
            {
                Logger.Write(functionName + "Error: " + ex.ToString(), "Error", 5, 999, TraceEventType.Error);
                throw;
            }
            finally
            {
                Logger.Write(functionName + "Exit", "Trace", 3);
            }
        }

        private FindFolderType BuildGetFolderIdQuery(string folderName)
        {
            string functionName = GetType().ToString() + " BuildGetFolderIdQuery: ";
            Logger.Write(functionName + "Enter", "Trace", 3);
            try
            {
                DistinguishedFolderIdType disFolderIdType = new DistinguishedFolderIdType();
                disFolderIdType.Id = DistinguishedFolderIdNameType.msgfolderroot;
                FindFolderType findFolderType = new FindFolderType();
                findFolderType.Traversal = FolderQueryTraversalType.Deep;
                findFolderType.ParentFolderIds = new DistinguishedFolderIdType[] { disFolderIdType }; //can be more than one type of folder
                findFolderType.FolderShape = new FolderResponseShapeType();
                findFolderType.FolderShape.BaseShape = DefaultShapeNamesType.IdOnly; //what we are actually after 
                findFolderType.Restriction = new RestrictionType();
                ContainsExpressionType containsExpression = new ContainsExpressionType();
                containsExpression.ContainmentMode = ContainmentModeType.FullString;
                containsExpression.ContainmentModeSpecified = true;
                containsExpression.ContainmentComparisonSpecified = true;
                PathToUnindexedFieldType fieldFolderName = new PathToUnindexedFieldType();
                fieldFolderName.FieldURI = UnindexedFieldURIType.folderDisplayName;
                containsExpression.Item = fieldFolderName;
                containsExpression.Constant = new ConstantValueType();
                containsExpression.Constant.Value = folderName;
                findFolderType.Restriction.Item = containsExpression;
                return findFolderType;
            }
            catch (Exception ex)
            {
                Logger.Write(functionName + "Error: " + ex.ToString(), "Error", 5, 999, TraceEventType.Error);
                throw;
            }
            finally
            {
                Logger.Write(functionName + "Exit", "Trace", 3);
            }
        }

        public string GetEmailName(string FromEmail, string Subject)
        {
            string functionName = GetType().ToString() + " GetEmailName: ";
            Logger.Write(functionName + "Enter", "Trace", 3);
            try
            {
                FindItemType findItemType = BuildGetEmailNameQuery(FromEmail, Subject);
                FindItemResponseType firt = _service.FindItem(findItemType);
                if (firt.ResponseMessages.Items.Length == 0)
                    return string.Empty;

                foreach (FindItemResponseMessageType firmtMessage in firt.ResponseMessages.Items)
                    if (firmtMessage.RootFolder != null && firmtMessage.RootFolder.TotalItemsInView > 0)
                    {
                        //We're only interested in the first one
                        ItemType itemType = ((ArrayOfRealItemsType)firmtMessage.RootFolder.Item).Items[0];
                        PathToUnindexedFieldType ptuftHeaders = new PathToUnindexedFieldType();
                        ptuftHeaders.FieldURI = UnindexedFieldURIType.itemInternetMessageHeaders;
                        PathToExtendedFieldType ptuftHeadersProp = new PathToExtendedFieldType();
                        ptuftHeadersProp.PropertyTag = "0x007D"; // PR_TRANSPORT_MESSAGE_HEADERS
                        ptuftHeadersProp.PropertyType = MapiPropertyTypeType.String;
                        //Have to do a GetItem to retrieve more details regarding the email than FindItem returns
                        GetItemType getItemRequest = new GetItemType();
                        getItemRequest.ItemIds = new ItemIdType[] { itemType.ItemId };
                        getItemRequest.ItemShape = new ItemResponseShapeType();
                        getItemRequest.ItemShape.BaseShape = DefaultShapeNamesType.AllProperties;
                        GetItemResponseType girt = _service.GetItem(getItemRequest);
                        if (girt.ResponseMessages.Items.Length == 0)
                            return string.Empty;
                        ItemInfoResponseMessageType grmtMessage = (ItemInfoResponseMessageType)girt.ResponseMessages.Items[0];
                        MessageType item = (MessageType)grmtMessage.Items.Items[0];
                        return item.From.Item.Name;
                    }
                return string.Empty;
            }
            catch (Exception ex)
            {
                Logger.Write(functionName + "Warning: Could not get name from email: " + ex.ToString(), "Warning", 4, 999, TraceEventType.Warning);
                return string.Empty;
            }
            finally
            {
                Logger.Write(functionName + "Exit", "Trace", 3);
            }
        }

        public Dictionary<string, string> GetEmailCCNames(string FromEmail, string Subject)
        {
            string functionName = GetType().ToString() + " GetEmailCCNames: ";
            Logger.Write(functionName + "Enter", "Trace", 3);
            try
            {
                Dictionary<string, string> ccNameAddressList = new Dictionary<string, string>();
                FindItemType findItemType = BuildGetEmailNameQuery(FromEmail, Subject);
                FindItemResponseType firt = _service.FindItem(findItemType);
                if (firt.ResponseMessages.Items.Length == 0)
                    return new Dictionary<string, string>();

                foreach (FindItemResponseMessageType firmtMessage in firt.ResponseMessages.Items)
                    if (firmtMessage.RootFolder != null && firmtMessage.RootFolder.TotalItemsInView > 0)
                    {
                        //We're only interested in the first one
                        ItemType itemType = ((ArrayOfRealItemsType)firmtMessage.RootFolder.Item).Items[0];
                        PathToUnindexedFieldType ptuftHeaders = new PathToUnindexedFieldType();
                        ptuftHeaders.FieldURI = UnindexedFieldURIType.itemInternetMessageHeaders;
                        PathToExtendedFieldType ptuftHeadersProp = new PathToExtendedFieldType();
                        ptuftHeadersProp.PropertyTag = "0x007D"; // PR_TRANSPORT_MESSAGE_HEADERS
                        ptuftHeadersProp.PropertyType = MapiPropertyTypeType.String;
                        //Have to do a GetItem to retrieve more details regarding the email than FindItem returns
                        GetItemType getItemRequest = new GetItemType();
                        getItemRequest.ItemIds = new ItemIdType[] { itemType.ItemId };
                        getItemRequest.ItemShape = new ItemResponseShapeType();
                        getItemRequest.ItemShape.BaseShape = DefaultShapeNamesType.AllProperties;
                        GetItemResponseType girt = _service.GetItem(getItemRequest);
                        if (girt.ResponseMessages.Items.Length == 0)
                            return new Dictionary<string, string>();
                        ItemInfoResponseMessageType grmtMessage = (ItemInfoResponseMessageType)girt.ResponseMessages.Items[0];
                        MessageType item = (MessageType)grmtMessage.Items.Items[0];

                        foreach (EmailAddressType address in item.CcRecipients)
                            ccNameAddressList.Add(address.EmailAddress, address.Name);
                    }
                return ccNameAddressList;
            }
            catch (Exception ex)
            {
                Logger.Write(functionName + "Warning: " + ex.ToString(), "Warning", 4, 999, TraceEventType.Warning);
                return new Dictionary<string, string>();
            }
            finally
            {
                Logger.Write(functionName + "Exit", "Trace", 3);
            }
        }


        private FindItemType BuildGetEmailNameQuery(string FromEmail, string Subject)
        {
            string functionName = GetType().ToString() + " BuildGetEmailNameQuery: ";
            Logger.Write(functionName + "Enter", "Trace", 3);
            try
            {
                //Define findItem object
                FindItemType findItemType = new FindItemType();
                findItemType.ItemShape = new ItemResponseShapeType { BaseShape = DefaultShapeNamesType.Default };
                findItemType.Traversal = ItemQueryTraversalType.Shallow;

                //Only get items from the inbox
                DistinguishedFolderIdType[] folderIDs = new DistinguishedFolderIdType[1];
                folderIDs[0] = new DistinguishedFolderIdType();
                folderIDs[0].Id = DistinguishedFolderIdNameType.inbox;
                findItemType.ParentFolderIds = folderIDs;

                //Retrieve only emails from specified email & subject
                RestrictionType restrictionType = new RestrictionType();
                //Sender restriction
                IsEqualToType addressIsEqualTo = new IsEqualToType();
                PathToUnindexedFieldType snSender = new PathToUnindexedFieldType();
                snSender.FieldURI = UnindexedFieldURIType.messageFrom;
                FieldURIOrConstantType ciConstantType = new FieldURIOrConstantType();
                ConstantValueType cvConstantValueType = new ConstantValueType();
                cvConstantValueType.Value = FromEmail;
                ciConstantType.Item = cvConstantValueType;
                addressIsEqualTo.Item = snSender;
                addressIsEqualTo.FieldURIOrConstant = ciConstantType;
                //Subject restriction
                IsEqualToType subjectIsEqualTo = new IsEqualToType();
                PathToUnindexedFieldType snSubject = new PathToUnindexedFieldType();
                snSubject.FieldURI = UnindexedFieldURIType.itemSubject;
                ciConstantType = new FieldURIOrConstantType();
                cvConstantValueType = new ConstantValueType();
                cvConstantValueType.Value = Subject;
                ciConstantType.Item = cvConstantValueType;
                subjectIsEqualTo.Item = snSubject;
                subjectIsEqualTo.FieldURIOrConstant = ciConstantType;
                AndType anAndType = new AndType();
                anAndType.Items = new SearchExpressionType[2];
                anAndType.Items[0] = addressIsEqualTo;
                anAndType.Items[1] = subjectIsEqualTo;
                restrictionType.Item = anAndType;
                findItemType.Restriction = restrictionType;
                return findItemType;
            }
            catch (Exception ex)
            {
                Logger.Write(functionName + "Error: " + ex.ToString(), "Error", 5, 999, TraceEventType.Error);
                throw;
            }
            finally
            {
                Logger.Write(functionName + "Exit", "Trace", 3);
            }
        }

        public string GetEmailHeaders(string FromEmail, string Subject)
        {
            string functionName = GetType().ToString() + " GetEmailHeaders: ";
            Logger.Write(functionName + "Enter", "Trace", 3);
            try
            {
                FindItemType findItemType = BuildGetEmailHeadersQuery(FromEmail, Subject);
                FindItemResponseType firt = _service.FindItem(findItemType);
                if (firt.ResponseMessages == null || firt.ResponseMessages.Items == null || firt.ResponseMessages.Items.Length == 0)
                    return string.Empty;
                FindItemResponseMessageType firmtMessage = (FindItemResponseMessageType)firt.ResponseMessages.Items[0];
                if (firmtMessage.RootFolder == null || firmtMessage.RootFolder.TotalItemsInView == 0)
                    return string.Empty;
                //We're only interested in the first email if more than one was found
                ItemType originalEmail = ((ArrayOfRealItemsType)firmtMessage.RootFolder.Item).Items[0];
                //Use the GetItem method to retrieve the full header properties
                GetItemResponseType responseWithEmailHeaderProperties = GetExtendedHeaderPropertiesFromEmail(originalEmail);
                if (responseWithEmailHeaderProperties.ResponseMessages == null || responseWithEmailHeaderProperties.ResponseMessages.Items == null || responseWithEmailHeaderProperties.ResponseMessages.Items.Length == 0)
                    return string.Empty;
                ItemType emailWithHeaderProperties = ((ItemInfoResponseMessageType)responseWithEmailHeaderProperties.ResponseMessages.Items[0]).Items.Items[0];
                if (emailWithHeaderProperties.ExtendedProperty == null || emailWithHeaderProperties.InternetMessageHeaders == null)
                    return string.Empty;

                GetItemType getItemRequest = BuildGetEmailHeaderPropertiesQuery(originalEmail, emailWithHeaderProperties);
                GetItemResponseType responseWithFullEmailHeaders = _service.GetItem(getItemRequest);
                
                string headers = string.Empty;
                foreach (ItemInfoResponseMessageType responseMessageType in responseWithFullEmailHeaders.ResponseMessages.Items)
                {
                    ItemType emailWithFullHeaders = responseMessageType.Items.Items[0];
                    if (emailWithFullHeaders.InternetMessageHeaders != null)
                        foreach (InternetHeaderType header in emailWithFullHeaders.InternetMessageHeaders)
                            if (header.HeaderName != null && header.Value != null)
                                headers += header.HeaderName + ": " + header.Value.ToString() + Environment.NewLine;
                }
                return headers;
            }
            catch (Exception ex)
            {
                Logger.Write(functionName + "Error: " + ex.ToString(), "Error", 5, 999, TraceEventType.Error);
                return string.Empty;
            }
            finally
            {
                Logger.Write(functionName + "Exit", "Trace", 3);
            }
        }

        private GetItemType BuildGetEmailHeaderPropertiesQuery(ItemType originalEmail, ItemType emailWithHeaderProperties)
        {
            string functionName = GetType().ToString() + " BuildGetEmailHeaderPropertiesQuery: ";
            Logger.Write(functionName + "Enter", "Trace", 3);
            try
            {
                PathToIndexedFieldType[] headerProperties = new PathToIndexedFieldType[emailWithHeaderProperties.InternetMessageHeaders.Length];
                int index = 0;
                foreach (InternetHeaderType internetHeaderType in emailWithHeaderProperties.InternetMessageHeaders)
                {
                    PathToIndexedFieldType headerProp = new PathToIndexedFieldType();
                    headerProp.FieldURI = DictionaryURIType.itemInternetMessageHeader;
                    headerProp.FieldIndex = internetHeaderType.HeaderName;
                    headerProperties[index++] = headerProp;
                }
                GetItemType getItemRequest = new GetItemType();
                getItemRequest.ItemIds = new ItemIdType[] { originalEmail.ItemId };
                getItemRequest.ItemShape = new ItemResponseShapeType();
                getItemRequest.ItemShape.BaseShape = DefaultShapeNamesType.IdOnly;
                getItemRequest.ItemShape.AdditionalProperties = headerProperties;
                return getItemRequest;
            }
            catch (Exception ex)
            {
                Logger.Write(functionName + "Error: " + ex.ToString(), "Error", 5, 999, TraceEventType.Error);
                throw;
            }
            finally
            {
                Logger.Write(functionName + "Exit", "Trace", 3);
            }
        }

        private GetItemResponseType GetExtendedHeaderPropertiesFromEmail(ItemType originalEmail)
        {
            string functionName = GetType().ToString() + " GetExtendedHeaderPropertiesFromEmail: ";
            Logger.Write(functionName + "Enter", "Trace", 3);
            try
            {
                PathToUnindexedFieldType ptuftHeaders = new PathToUnindexedFieldType();
                ptuftHeaders.FieldURI = UnindexedFieldURIType.itemInternetMessageHeaders;
                PathToExtendedFieldType ptuftHeadersProp = new PathToExtendedFieldType();
                ptuftHeadersProp.PropertyTag = "0x007D"; // PR_TRANSPORT_MESSAGE_HEADERS
                ptuftHeadersProp.PropertyType = MapiPropertyTypeType.String;
                GetItemType getItemRequest = new GetItemType();
                getItemRequest.ItemIds = new ItemIdType[] { originalEmail.ItemId };
                getItemRequest.ItemShape = new ItemResponseShapeType();
                getItemRequest.ItemShape.BaseShape = DefaultShapeNamesType.AllProperties;
                getItemRequest.ItemShape.AdditionalProperties = new BasePathToElementType[2];
                getItemRequest.ItemShape.AdditionalProperties[0] = ptuftHeaders;
                getItemRequest.ItemShape.AdditionalProperties[1] = ptuftHeadersProp;
                return  _service.GetItem(getItemRequest);
            }
            catch (Exception ex)
            {
                Logger.Write(functionName + "Error: " + ex.ToString(), "Error", 5, 999, TraceEventType.Error);
                throw;
            }
            finally
            {
                Logger.Write(functionName + "Exit", "Trace", 3);
            }
        }

        private FindItemType BuildGetEmailHeadersQuery(string FromEmail, string Subject)
        {
            string functionName = GetType().ToString() + " BuildGetEmailHeadersQuery: ";
            Logger.Write(functionName + "Enter", "Trace", 3);
            try
            {
                //Define findItem object
                FindItemType findItemType = new FindItemType();
                findItemType.ItemShape = new ItemResponseShapeType { BaseShape = DefaultShapeNamesType.Default };
                findItemType.Traversal = ItemQueryTraversalType.Shallow;
                //Only get items from the inbox
                DistinguishedFolderIdType[] folderIDs = new DistinguishedFolderIdType[1];
                folderIDs[0] = new DistinguishedFolderIdType();
                folderIDs[0].Id = DistinguishedFolderIdNameType.inbox;
                findItemType.ParentFolderIds = folderIDs;
                //Retrieve only emails from specified email & subject
                RestrictionType restrictionType = new RestrictionType();
                //Sender restriction
                IsEqualToType addressIsEqualTo = new IsEqualToType();
                PathToUnindexedFieldType snSender = new PathToUnindexedFieldType();
                snSender.FieldURI = UnindexedFieldURIType.messageFrom;
                FieldURIOrConstantType ciConstantType = new FieldURIOrConstantType();
                ConstantValueType cvConstantValueType = new ConstantValueType();
                cvConstantValueType.Value = FromEmail;
                ciConstantType.Item = cvConstantValueType;
                addressIsEqualTo.Item = snSender;
                addressIsEqualTo.FieldURIOrConstant = ciConstantType;
                //Subject restriction
                IsEqualToType subjectIsEqualTo = new IsEqualToType();
                PathToUnindexedFieldType snSubject = new PathToUnindexedFieldType();
                snSubject.FieldURI = UnindexedFieldURIType.itemSubject;
                ciConstantType = new FieldURIOrConstantType();
                cvConstantValueType = new ConstantValueType();
                cvConstantValueType.Value = Subject;
                ciConstantType.Item = cvConstantValueType;
                subjectIsEqualTo.Item = snSubject;
                subjectIsEqualTo.FieldURIOrConstant = ciConstantType;
                AndType anAndType = new AndType();
                anAndType.Items = new SearchExpressionType[2];
                anAndType.Items[0] = addressIsEqualTo;
                anAndType.Items[1] = subjectIsEqualTo;
                restrictionType.Item = anAndType;
                findItemType.Restriction = restrictionType;
                return findItemType;
            }
            catch (Exception ex)
            {
                Logger.Write(functionName + "Error: " + ex.ToString(), "Error", 5, 999, TraceEventType.Error);
                throw;
            }
            finally
            {
                Logger.Write(functionName + "Exit", "Trace", 3);
            }
        }
    }

    public class CustomReader : ExchangeServiceBinding
    {
        protected override XmlReader GetReaderForMessage(
                        SoapClientMessage message,
                        int bufferSize)
        {
            XmlReader retval = base.GetReaderForMessage(message, bufferSize);
            XmlTextReader xrt = retval as XmlTextReader;
            if (null != xrt)
            {
                xrt.Normalization = false;
            }
            return retval;
        }
    }
}
